/*
 * Copyright (c) 2011 Robert Kirchgessner <rkirchge@gmail.com>
 *                    Carlo Pascoe <carlo.pascoe@gmail.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <sys/queue.h>
#include <stdint.h>

#include "hashgraph.h"

void
hash_init(struct hash_graph *hg, struct overlaygraph *og, void (*hash_func)(struct subgraph *sg, hash_type *hash, uint32_t feature_size), uint32_t feature_size)
{
  uint32_t x, y;
  struct subgraph *sg, *sg_next;
  struct overlay_edge *oe;

  hg->size = og->size;

  //Hash adjacency list
  hg->hash_adj = (uint32_t *)malloc(sizeof(uint32_t)*og->size*og->size);
  assert(hg->hash_adj != NULL);
  memset(hg->hash_adj, 0, sizeof(uint32_t)*og->size*og->size);

  hg->subgraph_array = (struct subgraph **)malloc(og->size*sizeof(struct subgraph *));
 
  //Linear hash array
  hg->hash_edges = (uint32_t *)malloc(sizeof(uint32_t)*og->size);
  assert(hg->hash_edges != NULL);
  memset(hg->hash_edges, 0, sizeof(uint32_t)*og->size);
 
  //Linear hash array
  hg->hash_array = (hash_type *)malloc(sizeof(hash_type)*og->size);
  assert(hg->hash_array != NULL);
 
  x = 0;
  LIST_FOREACH(sg, &og->subgraphs, next) {
    hash_func(sg, &hg->hash_array[x], feature_size);
    hg->subgraph_array[x] = sg;
    y = x;
    sg_next = sg;
    while(sg_next) {
      oe = overlay_get_edge(sg, sg_next);
      if(oe != NULL) {
        //Build adjacency matrix entry
        hg->hash_adj[COORD(x,y,og->size)] = oe->flux;
        hg->hash_adj[COORD(y,x,og->size)] = oe->flux;
        hg->hash_edges[x] += 1;
        hg->hash_edges[y] += 1;
      }
      y++; 
      sg_next = LIST_NEXT(sg_next, next);     
    }
    x++;
  }
}

void
hash_cleanup(struct hash_graph *hg)
{
  free(hg->hash_adj);
  free(hg->hash_array);
  free(hg->hash_edges);
  free(hg->subgraph_array);
}

uint32_t
hash_dist(hash_type h1, hash_type h2)
{
  return ((h1>h2)?h1-h2:h2-h1);
}

void
hash_write_dotty(const char *fname, struct hash_graph *hg)
{
  uint32_t x, y;
  FILE *fp = fopen(fname, "w");

  fprintf(fp,"graph hashgraph {\n");

  for(x=0; x<hg->size; x++) {
    fprintf(fp, "node%d [label=\"0x%x\"]\n", x, hg->hash_array[x]);
  }


  for(x=0; x < hg->size; x++) {
    for(y=x; y<hg->size; y++) {
      if(hg->hash_adj[COORD(x,y,hg->size)] != 0) 
      fprintf(fp, "node%d -- node%d [label=\"%d\"]\n", x, y, hg->hash_adj[COORD(x,y,hg->size)]);
    }
  }
 
  fprintf(fp,"}\n\n");

}

void
hash_write_dotty_align(const char *fname, uint32_t *map_hg, struct hash_graph *hg, uint32_t *map_q, struct hash_graph *hgq)
{
  uint32_t x, y;
  FILE *fp = fopen(fname, "w");

  fprintf(fp,"graph hashgraph {\n");

  fprintf(fp, "subgraph cluster_0 {\n");
  fprintf(fp, "label = \"Query Hash Tree\"\n");
  for(x=0; x<hgq->size; x++) {
    fprintf(fp, "query%d [label=\"0x%x (%d)\"]\n", x, hgq->hash_array[x], x);
  }

  for(x=0; x < hgq->size; x++) {
    for(y=x; y<hgq->size; y++) {
      if(hgq->hash_adj[COORD(x,y,hgq->size)] != 0) 
      fprintf(fp, "query%d -- query%d [label=\"%d\"]\n", x, y, hgq->hash_adj[COORD(x,y,hgq->size)]);
    }
  }

  fprintf(fp, "}\n");

  fprintf(fp, "subgraph cluster_1 {\n");
;
  fprintf(fp, "label = \"Graph Hash Tree\"\n");

  for(x=0; x<hg->size; x++) {
    fprintf(fp, "node%d [label=\"0x%x (%d)\"]\n", x, hg->hash_array[x],x );
  } 

  for(x=0; x < hg->size; x++) {
    for(y=x; y<hg->size; y++) {
      if(hg->hash_adj[COORD(x,y,hg->size)] != 0) 
      fprintf(fp, "node%d -- node%d [label=\"%d\"]\n", x, y, hg->hash_adj[COORD(x,y,hg->size)]);
    }
  }
  fprintf(fp, "}\n");

  //Write out the mappings
  for(x=0; x < hgq->size; x++) {
    fprintf(fp, "node%d -- query%d [style=\"dashed,bold\" color=\"red\"]\n", map_hg[x], map_q[x]);
  }
 
  fprintf(fp,"}\n\n");


}

